/* 分析
1.promis 就是一个类 执行这个类的时候 需要传递一个执行器 执行器会立即执行
2.有三种状态
    -fulfillde
    -rejected
    -peding
    -状态一旦确定就不可更改
3.resolve和reject函数是用来更改状态的
    -resolve：fulfillde
    -reject：rejected
4.then方法内部做的事情就是判断状态 如果是成功就调用成功的回调，如果是失败就调用失败的回调
5.then成功方法有一个参数表示成功之后的值，then失败方法回调有个参数表示失败的原因
6.同一个promise对象的then方法是可以调用多次的
7.then方法可以被链式调用，后面的then方法回调函数拿到的值是上一个then方法的回调函数
8.all 方法所有成功返回成功 如果有一个失败则返回第一个失败的值
9.resolve 方法 可以创建一个promise接收一个参数传入的值就是promise的返回值
10.race 只要有一个promise返回就取消其他的使用先返回的值
11.finally方法不论当前promise对象方法是成功还是失败都finally回调会执行一次，可以链式调用then方法
12.catch 方法 执行promise失败时的回调
*/
const PENDING = "pending"; //等待
const FULFILLED = "fulfilled"; //成功
const REJECTED = "rejected"; //失败

class MyPromise {
  constructor(executor) {
    //通过try捕获执行器异常
    try {
      executor(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }
  //成功的值
  vaule = undefined;
  //失败的值
  reason = undefined;
  //默认等待
  status = PENDING;
  //成功回调  数组存储可以调用多次
  seccessCallback = [];
  //失败回调
  failCallback = [];
  resolve = (value) => {
    //如果不是等待就阻止向下执行
    if (this.status !== PENDING) return;
    //将状态改为成功
    this.status = FULFILLED;
    //保存成功后的值
    this.vaule = value;
    //判断成功的回调是否存在 如果存在立即执行
    while (this.seccessCallback.length) {
      this.seccessCallback.shift()();
    }
  };
  reject = (reason) => {
    //如果不是等待就阻止向下执行
    if (this.status !== PENDING) return;
    //将状态改为失败
    this.status = REJECTED;
    //保存失败的原因
    this.reason = reason;
    //判断失败的回调是否存在 如果存在立即执行
    while (this.failCallback.length) {
      this.failCallback.shift()();
    }
  };
  then(seccessCallback, failCallback) {
    //处理then方法不传参数
    seccessCallback = seccessCallback ? seccessCallback : (v) => v;
    failCallback = failCallback
      ? failCallback
      : (r) => {
          throw r;
        };
    //通过new一个新的promis对象实现链式调用
    let promise = new MyPromise((resolve, reject) => {
      //判断状态
      if (this.status === FULFILLED) {
        //执行成功回调
        //改成宏任务保证在同步代码后执行
        setTimeout(() => {
          //捕获then方法内部异常
          try {
            let parameter = seccessCallback(this.vaule);
            resolvePromise(promise, parameter, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status === REJECTED) {
        //执行失败回调
        setTimeout(() => {
          //捕获then方法内部异常
          try {
            let parameter = failCallback(this.reason);
            resolvePromise(promise, parameter, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        //等待
        //将成功回调和失败回调存起来
        this.seccessCallback.push(() => {
          setTimeout(() => {
            //捕获then方法内部异常
            try {
              let parameter = seccessCallback(this.vaule);
              resolvePromise(promise, parameter, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            //捕获then方法内部异常
            try {
              let parameter = failCallback(this.reason);
              resolvePromise(promise, parameter, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    return promise;
  }
  catch(failCallback) {
    return this.then(undefined, failCallback);
  }
  finally(callback) {
    //回去当前对象的状态调用一次回调
    //返回一个promise对象
    return this.then(
      (v) => {
        //处理异步
        return MyPromise.resolve(callback()).then(() => v);
      },
      (r) => {
        return MyPromise.resolve(callback()).then(() => {
          throw r;
        });
      }
    );
  }
  static all(array) {
    let result = [];
    let index = 0;
    return new MyPromise((resolve, reject) => {
      /**
       * 存储每一个成功的值
       * @param {*} key
       * @param {*} val
       */
      function addData(key, val) {
        result[key] = val;
        index++;
        //判定循环是否执行完成
        if (index === array.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        //判定是否为Promise对象
        if (current instanceof MyPromise) {
          current.then(
            (v) => {
              addData(i, v);
            },
            (r) => {
              reject(r);
            }
          );
        } else {
          addData(i, array[i]);
        }
      }
    });
  }
  static race(array) {
    return new MyPromise(function (resolve, reject) {
      for (var i = 0; i < array.length; i++) {
        MyPromise.resolve(array[i]).then(resolve, reject);
      }
    });
  }
  static resolve(v) {
    //如果是Promise对象直接返回
    if (v instanceof MyPromise) return v;
    //如果不是则创建一个promise对象返回成功的值
    return new MyPromise((v) => this.resolve(v));
  }
}
/**
 * 处理then方法链式调用
 * @param {*} parameter
 * @param {*} resolve
 * @param {*} reject
 */
function resolvePromise(promise, parameter, resolve, reject) {
  //处理重复调用
  if (promise === parameter) {
    return reject(
      new TypeError("chaining cycle detected for promise #<Promise>")
    );
  }
  // 判断parameter的值是普通值还是promis对象
  if (parameter instanceof MyPromise) {
    // 如果是promis对象 则查看promis对象,再根据promis返回的结果在调用 resolve 还是reject
    parameter.then(resolve, reject);
  } else {
    // 是普通值直接调用resolve方法
    resolve(parameter);
  }
}
module.exports = MyPromise;
